package com.sun.config;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.commons.Constant;
import com.sun.commons.utils.JwtTokenUtil;
import com.sun.dto.users.LoginUserDetails;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.sun.commons.utils.JwtTokenUtil.CLAIM_KEY_TYPE;

/**
 * MP自动填充Handler
 *
 * @Author whsunchunbo@qq.com
 * @CreateTime 2025/9/8 20:23
 */
@Component
@Slf4j

public class JwtFilter extends OncePerRequestFilter {
    @Value("${jwt.header}")
    private String tokenHeader;
    @Value("${jwt.prefix}")
    private String tokenPrefix;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private JwtTokenUtil jwt;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {


        String authHeader = request.getHeader(tokenHeader);
        if (StrUtil.isBlank(authHeader) || !authHeader.startsWith(tokenPrefix)) {

            setTokenIssue("no token present", request);
            filterChain.doFilter(request, response);
            return;
        }
        ObjectMapper objectMapper = new ObjectMapper();

        String token = authHeader.replaceFirst(tokenPrefix + " ", "");
        Claims claims = jwt.getClaimsFromToken(token);
        String userId = claims.getSubject();
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        if (claims.get(CLAIM_KEY_TYPE) != null) {
            authorities.add(new SimpleGrantedAuthority(claims.get(CLAIM_KEY_TYPE).toString()));

        }

        if (claims.get("roles") != null) {
            List<String> roles = castList(claims.get("roles"), String.class);
            log.info("roles:{}", roles);

            for (String role : roles) {
                authorities.add(new SimpleGrantedAuthority(role));

                String key = Constant.REDIS_ROLE_PERMISSION_KEY + role;
                Object value = redisTemplate.opsForValue().get(key);
                if (value != null) {
                    String[] perms = objectMapper.readValue(value.toString(), String[].class);
                    for (String perm : perms) {
                        authorities.add(new SimpleGrantedAuthority(perm));
                    }
                }
            }

            LoginUserDetails userDetails = new LoginUserDetails();
            userDetails.setUserId(Long.valueOf(userId));
            userDetails.setUsername(claims.get("username").toString());
            userDetails.setName(claims.get("name").toString());
            UsernamePasswordAuthenticationToken unAuthenticated =
                    new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
            SecurityContextHolder.getContext().setAuthentication(unAuthenticated);
        }


        log.info("userId:{}", userId);
        filterChain.doFilter(request, response);
    }

    public static <T> List<T> castList(Object obj, Class<T> clazz) {
        List<T> result = new ArrayList<>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                result.add(clazz.cast(o));
            }
            return result;
        }
        return null;
    }

    private void setTokenIssue(String tokenIssue, HttpServletRequest request) {
        UsernamePasswordAuthenticationToken unAuthenticated =
                new UsernamePasswordAuthenticationToken("", "");

        SecurityContextHolder.getContext().setAuthentication(unAuthenticated);
        request.setAttribute("TOKEN_ISSUE", tokenIssue);
        log.warn("Token issue detected: {}", tokenIssue);
    }
}
